/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2020年9月3日
 */
package com.massyframework.beanskin.runtime.modular.assembly.handling;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import com.massyframework.beanskin.assembly.AssemblyConfig;
import com.massyframework.beanskin.assembly.AssemblyStatus;
import com.massyframework.beanskin.assembly.handling.DependencyServiceHandler;
import com.massyframework.beanskin.assembly.handling.DependencyServiceProvider;
import com.massyframework.beanskin.assembly.handling.HandlerBase;
import com.massyframework.beanskin.assembly.handling.LifecycleManager;
import com.massyframework.beanskin.service.DependencyServiceDescriptor;
import com.massyframework.beanskin.service.Filter;
import com.massyframework.beanskin.service.ServiceEvent;
import com.massyframework.beanskin.service.ServiceListener;
import com.massyframework.beanskin.service.ServiceReference;
import com.massyframework.beanskin.service.ServiceRegistedEvent;
import com.massyframework.beanskin.service.ServiceRepository;

/**
 * 提供依赖的匹配管理
 * 
 * <p>
 * {@code DependentOnMatching}监听{@link ServiceEvent}，识别判断哪些服务属于本省所
 * 需要的依赖服务，并在所有依赖服务均满足后，让装配件进入工作状态。
 */
class DependentOnMatching extends HandlerBase implements DependencyServiceHandler, DependencyServiceProvider {

	//未匹配资源
    private final Set<DependencyServiceDescriptor<?>> unmatchs = new HashSet<>();
    //已匹配资源
    private final Set<DependencyServiceDescriptor<?>> matcheds = new HashSet<>();
    //已匹配关系
    private final Map<DependencyServiceDescriptor<?>, ServiceReference<?>> matchedMap = new HashMap<>();

    private ListenerImpl listener;

    /**
     * 构造方法
     */
    public DependentOnMatching() {
    }

    /**
     * 初始化
     *
     * @throws Exception 发生非预期的例外
     */
    @Override
    protected void init() throws Exception {
        super.init();

        synchronized (this.getLifecycleManager()) {
            this.unmatchs.addAll(this.getDependencyServiceDescriptors());
            if (this.listener == null) {
                ServiceRepository repository =
                        ServiceRepository.retrieveFrom(this.getAssociatedAssembly());
                this.listener = new ListenerImpl(repository);
                repository.addListener(this.listener,
                        repository.createFilter(null));

                List<DependencyServiceDescriptor<?>> list =new ArrayList<>(this.unmatchs);
                for (DependencyServiceDescriptor<?> descriptor : list) {
                    this.doMatch(descriptor, repository);
                }
            }
        }
    }

    protected <S> void doMatch(DependencyServiceDescriptor<S> definition, ServiceRepository repo){
        Optional<ServiceReference<S>> optional =
                repo.findServiceReference(definition.getRequiredType(), definition.getFilterString());
        optional.ifPresent( reference -> this.listener.doMatch(reference, definition, false) );
    }

    /**
     * 析构方法
     */
    @Override
    public void destroy() {
        synchronized (this.getLifecycleManager()) {
            if (this.listener != null) {
                ServiceRepository repository =
                        ServiceRepository.retrieveFrom(this.getAssociatedAssembly());
                repository.removeListener(this.listener);
            }
            super.destroy();
        }
    }

    /**
     * 所有依赖服务定义
     *
     * @return {@link List}， 依赖服务定义集合
     */
    @Override
    public List<DependencyServiceDescriptor<?>> getDependencyServiceDescriptors() {
        AssemblyConfig config =
                this.getLifecycleManager().getAssociatedAssembly().getConfig();
        return config.getDependencyServiceDescriptors();
    }

    /**
     * 获取匹配依赖服务定义的服务引用
     *
     * @param definition 依赖服务定义
     * @return {@link ServiceReference},如果依赖服务未被匹配，则可能返回null.
     */
    @SuppressWarnings("unchecked")
	@Override
    public <S> ServiceReference<S> findMatchedServiceReference(DependencyServiceDescriptor<S> definition) {
        synchronized (this.getLifecycleManager()) {
            return (ServiceReference<S>) this.matchedMap.get(definition);
        }
    }

    /**
     * 所有未匹配的依赖服务定义
     *
     * @return {@link List}, 依赖服务定义集合
     */
    @Override
    public Collection<DependencyServiceDescriptor<?>> getUnmatchDependencyServiceDescriptors() {
        synchronized (this.getLifecycleManager()) {
            return Collections.unmodifiableCollection(this.unmatchs);
        }
    }

    /* (non-Javadoc)
     * @see com.massyframework.assembly.extension.DependencyServiceProvider#getDependencyServices()
     */
    @Override
    public Map<DependencyServiceDescriptor<?>, Object> getDependencyServices() {
        if (this.getLifecycleManager().getAssemblyStatus() == AssemblyStatus.READIED) {
            synchronized(this.getLifecycleManager()) {
                final Map<DependencyServiceDescriptor<?>, Object> result = new HashMap<>();
                if (this.unmatchs.isEmpty()) {
                    ServiceRepository serviceRepository =
                            ServiceRepository.retrieveFrom(this.getAssociatedAssembly());
                    for (Map.Entry<DependencyServiceDescriptor<?>, ServiceReference<?>> entry: this.matchedMap.entrySet()) {
                        ServiceReference<?> reference = entry.getValue();

                        Optional<?> optional = serviceRepository.getService(reference);
                        optional.ifPresent(service -> result.put(entry.getKey(), service ));
                    }
                    return result;
                }
            }
        }
        return Collections.emptyMap();
    }

    /**
     * 启动装配件
     */
    private void startAssembly(){
        if (this.isAllMatched()){
            try{
                LifecycleManager handler = this.getLifecycleManager();
                handler.start();
            }catch(Exception e){
                logError("start " + this.getAssociatedAssembly() + " failed.", e);
            }
        }
    }

    /**
     * 启动装配件
     */
    private void stopAssembly(){
        try{
            this.getLifecycleManager().stop();
        }catch(Exception e){
            logError("stop " + this.getAssociatedAssembly() + " failed.", e);
        }
    }

    /**
     * 根据<code>reference</code>获取被匹配的依赖服务描述
     * @param reference 服务引用
     * @return {@link DependencyServiceDefinition}
     */
    @SuppressWarnings("unchecked")
    protected <S> DependencyServiceDescriptor<S> getMatchedDescriptor(ServiceReference<S> reference){
        synchronized (this.getLifecycleManager()) {
            if (this.matchedMap.containsValue(reference)) {
                for (Map.Entry<DependencyServiceDescriptor<?>, ServiceReference<?>> entry :
                        this.matchedMap.entrySet()) {
                    if (entry.getValue() == reference) {
                        return (DependencyServiceDescriptor<S>) entry.getKey();
                    }
                }
            }
        }

        return null;
    }

    /**
     * 是否所有的依赖都已经匹配
     *
     * @return {@link boolean},返回<code>true</code>表示全部匹配，否则返回<code>false</code>
     */
    @Override
    public boolean isAllMatched() {
        synchronized (this.getLifecycleManager()) {
            return this.unmatchs.isEmpty();
        }
    }

    private class ListenerImpl implements ServiceListener {

        private Map<Class<?>, Map<DependencyServiceDescriptor<?>, Filter>> filterMap;

        /**
         * 构造方法
         */
        public ListenerImpl(ServiceRepository repository) {
            this.filterMap = this.createFilterMap(repository);
        }

        /**
         * 服务注册处理
         * @param reference
         * @param map
         */
        private void doRegisted(ServiceReference<?> reference,
                                Map<DependencyServiceDescriptor<?>, Filter> map){
            for (Map.Entry<DependencyServiceDescriptor<?>, Filter> entry: map.entrySet()){
                if (entry.getValue().match(reference)){
                    this.doMatch(reference, entry.getKey(), true);
                }
            }
        }

        /**
         * 服务注销处理
         * @param reference
         * @param map
         */
        private void doUnregisting(ServiceReference<?> reference,
                                   Map<DependencyServiceDescriptor<?>, Filter> map){
            //准备注销
        	DependencyServiceDescriptor<?> descriptor =
                    getMatchedDescriptor(reference);
            if (descriptor != null){
                synchronized (DependentOnMatching.this.getLifecycleManager()) {
                    matchedMap.remove(descriptor);
                    matcheds.remove(descriptor);
                    unmatchs.add(descriptor);

                    logTrace("unmatched dependency: " + descriptor + ".");

                }
                stopAssembly();
            }
        }

        /**
         * 执行匹配
         * @param matchedReference 匹配的服务服务引用
         * @param definition 定义
         */
        public void doMatch(ServiceReference<?> matchedReference, DependencyServiceDescriptor<?> definition, boolean autoStart){
            boolean matched = false;
            synchronized (DependentOnMatching.this.getLifecycleManager()) {
                if (matchedMap.putIfAbsent(definition, matchedReference) == null) {
                    matcheds.add(definition);
                    unmatchs.remove(definition);

                    logTrace("matched dependency: " + definition + ".");
                    matched = true;
                }
            }

            if (autoStart) {
	            if (matched){
	                startAssembly();
	            }
            }
        }

        /**
         * 当<code>event</code>事件触发时，对事件进行处理
         *
         * @param event {@link ServiceEvent}
         */
        @Override
        public void onChanged(ServiceEvent<?> event) {
            Class<?>[] objectTypes =
                    event.getServiceReference().getExportTypes();
            try {
                for (Class<?> type: objectTypes){
                    Map<DependencyServiceDescriptor<?>, Filter> map = this.filterMap.get(type);
                    if (map != null){
                        if (event instanceof ServiceRegistedEvent){
                            this.doRegisted(event.getServiceReference(), map);
                        }else{
                            this.doUnregisting(event.getServiceReference(), map);
                        }
                    }
                }
            }catch(Exception e) {
                throw e;
            }
        }

        /**
         * 创建过滤Map
         * @param repository 服务仓储
         */
        protected Map<Class<?>, Map<DependencyServiceDescriptor<?>, Filter>> createFilterMap(
                ServiceRepository repository){
            Map<Class<?>, Map<DependencyServiceDescriptor<?>, Filter>> result = new HashMap<>();

            List<DependencyServiceDescriptor<?>> descriptors =
                    getDependencyServiceDescriptors();
            for (DependencyServiceDescriptor<?> descriptor: descriptors){
                Class<?> type = descriptor.getRequiredType();
                Filter filter = repository.createFilter(descriptor.getFilterString());

                Map<DependencyServiceDescriptor<?>, Filter> map = result.get(type);
                if (map == null){
                    map = new HashMap<>();
                    result.put(type, map);
                }
                map.put(descriptor, filter);
            }

            return result;
        }
    }

}
